﻿using hyjx.comm.Exceptions;
using hyjx.comm.Interfaces;
using hyjx.comm;
using Hyjx.DeliveryService.Configs;
using Hyjx.GanFengLogger;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Net.WebRequestMethods;
using Hyjx.DeliveryService.Objects;
using System.Collections.Concurrent;
using S7.Net;
using Serilog;
using Serilog.Events;

namespace Hyjx.DeliveryService
{
    public class Delivery : BackgroundService
    {
        private string SerilogOutputTemplate = "[{Timestamp:yyy/MM/dd HH:mm:ss.fff} {Level:u3}] {Message:lj} {NewLine}{Exception}";

        readonly ILogger<Delivery>? _logger;
        readonly IConfiguration? _config;

        DeliverieConfig[]? _deliveryConfigs;
        int _reportSeconds;
        string? _webApiBaseUrl;
        string? _storageJarSlurryInfo;
        AppHttp? _http;
        ConcurrentDictionary<string, S7Protocol> _protocols = new();

        public Delivery(IConfiguration config, ILogger<Delivery> logger)
        {
            try
            {
                _config = config;
                _logger = logger;

                Log.Logger = new LoggerConfiguration()
                            .Enrich.FromLogContext()
                            .MinimumLevel.Debug()
                            .WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p =>
                            p.Level == LogEventLevel.Information).
                            WriteTo.File($"{AppDomain.CurrentDomain.BaseDirectory}/Log/{DateTime.Now:yyyyMM}/Info-.txt", rollingInterval: RollingInterval.Day,
                            rollOnFileSizeLimit: true,
                            fileSizeLimitBytes: 20_000_000,
                            retainedFileTimeLimit: TimeSpan.FromDays(90),
                            shared: true,
                            outputTemplate: SerilogOutputTemplate))
                            .WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Error).WriteTo.File($"{AppDomain.CurrentDomain.BaseDirectory}/Log/{DateTime.Now:yyyyMM}/Error-.txt", rollingInterval: RollingInterval.Day, rollOnFileSizeLimit: true,
                            fileSizeLimitBytes: 20_000_000,
                            retainedFileTimeLimit: TimeSpan.FromDays(90),
                            shared: true,
                            outputTemplate: SerilogOutputTemplate))
                            .CreateLogger();
            }
            catch (Exception ex)
            {
                //if (_logger == null)
                //    return;

                //GFLogger log = new(_logger, string.Empty, -1, string.Empty, string.Empty, string.Empty);
                //log.Append("浆料输送系统服务无法启动");
                //log.CompleteWithError(ex);

                Log.Error(ex, "浆料输送系统服务启动异常");
            }
        }

        public override Task StartAsync(CancellationToken cancellationToken)
        {
            if (_config == null || _logger == null)
                return Task.CompletedTask;

            _deliveryConfigs = _config.GetSection("Deliveries").Get<DeliverieConfig[]>();
            _reportSeconds = _config.GetValue<int>("ReportSeconds");
            _webApiBaseUrl = _config.GetValue<string>("WebApiBaseUrl");
            _storageJarSlurryInfo = _config.GetValue<string>("StorageJarSlurryInfo");
            _http = new(_logger);

            if (_deliveryConfigs == null)
                return Task.CompletedTask;

            foreach (var plc in _deliveryConfigs.Where(p => p.IsEnabled))
            {
                TryAddToProtocols(plc, null);
            }

            Log.Information("浆料输送系统服务已启动。");
            return base.StartAsync(cancellationToken);
        }

        public override Task StopAsync(CancellationToken cancellationToken)
        {
            _deliveryConfigs = null;
            _reportSeconds = 0;
            _webApiBaseUrl = null;
            _http = null;
            _protocols.Clear();
            //_logger?.LogInformation("浆料输送系统服务已停止。");
            Log.Information("浆料输送系统服务已停止。");
            Log.CloseAndFlush();
            return base.StopAsync(cancellationToken);
        }

        protected override Task ExecuteAsync(CancellationToken stoppingToken)
        {
            int elapsedReportSeconds = 1;
            while (!stoppingToken.IsCancellationRequested)
            {
                if (elapsedReportSeconds == _reportSeconds)
                {
                    Task.Run(ReportStatus, stoppingToken);
                    elapsedReportSeconds = 1;
                }
                else
                    elapsedReportSeconds++;

                Thread.Sleep(TimeSpan.FromSeconds(1));
            }

            return Task.CompletedTask;
        }

        S7Protocol TryAddToProtocols(DeliverieConfig plc, S7Protocol? protocol)
        {
            if (protocol == null)
            {
                protocol = new(plc.CpuType, plc.Ip, plc.Rack, plc.Slot);
                try
                {
                    protocol.Connect();
                    _protocols.TryAdd(plc.Ip, protocol);
                }
                catch (S7ProtocolConnectException ex)
                {
                    Log.Error(ex, "无法连接到 PLC");
                }
            }
            return protocol;
        }


        private void ReportStatus()
        {
            if (_http == null || string.IsNullOrEmpty(_webApiBaseUrl) || _deliveryConfigs == null || _logger == null)
                return;

            string url = $"{_webApiBaseUrl}/api/StorageJar/SlurryInfo";

            foreach (var device in _deliveryConfigs.Where(p => p.IsEnabled))
            {
                if (string.IsNullOrEmpty(device.Ip))
                    continue;

                if (device.ArtifactTanks == null)
                    continue;

                try
                {
                    _protocols.TryGetValue(device.Ip, out S7Protocol? plc);
                    plc = TryAddToProtocols(device, plc);
                    if (!plc.IsConnected)
                        plc.Connect();

                    foreach (var item in device.ArtifactTanks)
                    {

                        Task.Run(() =>
                        {
                            try
                            {
                                ReportStatusArgs args = new()
                                {
                                    DeviceCode = item.DeviceCode
                                };
                                if (!string.IsNullOrEmpty(item.PressureAddress))
                                    args.Pressure = Math.Round(plc.ReadFloat(item.PressureAddress), 2);
                                if (!string.IsNullOrEmpty(item.TemperatureAddress))
                                    args.Temperature = Math.Round(plc.ReadFloat(item.TemperatureAddress), 2);
                                if (!string.IsNullOrEmpty(item.WeightAddress))
                                    args.Weight = Math.Round(plc.ReadFloat(item.WeightAddress), 2);
                                if (!string.IsNullOrEmpty(item.SpeedAddress))
                                    args.Speed = Math.Round(plc.ReadFloat(item.SpeedAddress), 2);

                                _http.ReportStatus(_webApiBaseUrl + _storageJarSlurryInfo, args);
                            }
                            catch (S7ProtocolReadException ex)
                            {
                                Log.Error(ex, $"读取成品或中转罐{item.DeviceCode}状态异常。");
                            }
                            catch (Exception ex)
                            {
                                Log.Error(ex, $"读取成品或中转罐{item.DeviceCode}状态异常。");
                            }
                        });

                    }

                }
                catch (Exception ex)
                {
                    Log.Error(ex, $"读取 IP: {device.Ip} 数据异常");
                }
            }

        }
    }
}
